package com.google.jessewilson.codesearch.api;

import com.google.inject.Inject;
import com.google.jessewilson.codesearch.api.spi.SearchProviderBackend;
import com.google.jessewilson.codesearch.util.EventListSink;
import com.google.jessewilson.codesearch.util.Progress;

import java.util.concurrent.ExecutorService;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Populate the results of a query.
 *
 * @author jessewilson
 */
public class DefaultResultsPopulator implements ResultsPopulator {

  private final ExecutorService executorService;
  private final DefaultQueryResponse queryResponse;
  private final SearchProviderBackend searchProviderBackend;

  /** where to start fetching the next set of results */
  private int nextResultStart = 0;
  private int pageSize = 250;

  /** the state of the search provider */
  State searchProviderState;


  @Inject
  public DefaultResultsPopulator(
      ExecutorService executorService,
      DefaultQueryResponse queryResponse,
      SearchProviderBackend searchProviderBackend) {
    this.executorService = executorService;
    this.queryResponse = queryResponse;
    this.searchProviderBackend = searchProviderBackend;
  }

  public synchronized State getState() {
    return searchProviderState;
  }

  private Page nextPage() {
    int end = nextResultStart + pageSize;
    Page result = new Page(nextResultStart, end);
    nextResultStart = end;
    return result;
  }

  public synchronized void fetchMoreResults() {
    if (noMoreResults()) {
      return;
    }

    final Query query = queryResponse.getQuery();
    final Page page = nextPage();

    // enter the downloading state for this search provider
    final Progress.Task work = queryResponse.getProgress().startTask();
    searchProviderState = State.FETCHING;

    // do the search
    executorService.submit(new Runnable() {
      public void run() {
        State newSearchProviderState = State.ALL_RESULTS_DOWNLOADED;
        try {
          SearchResult searchResult = searchProviderBackend.search(query,
              new EventListSink<Line>(queryResponse.getLines()), page);
          newSearchProviderState = searchResult.getHasMoreResults()
              ? State.RESULTS_OUTSTANDING
              : State.ALL_RESULTS_DOWNLOADED;
        } catch(SearchProviderException e) {
          Logger.getLogger(DefaultResultsPopulator.class.getName()).log(
              Level.WARNING, "Failed to search for " + query.getSearchTerm() +
              " using " + searchProviderBackend.getClass(), e);
        } finally {
          // exit the downloading state for this search provider
          synchronized(DefaultResultsPopulator.this) {
            searchProviderState = newSearchProviderState;
            work.finished();
          }
        }
      }
    });
  }

  private boolean noMoreResults() {
    return searchProviderState != null &&
        searchProviderState != State.RESULTS_OUTSTANDING;
  }
}
